﻿using Encloser.Packed;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

namespace Encloser.Json
{
    public static class JsonParser
    {
        #region Object To ?
        public static Result<string> ToJsonString(object obj)
        {
            if (obj == null) return Result<string>.Success("null");
            var sb = new StringBuilder();
            try
            {
                ToString(obj, sb);
                return Result<string>.Success(sb.ToString());
            }
            catch (Exception e) { return Result<string>.Failed(e); }
        }

        public static Result ToJsonStream(object obj, Stream stream)
        {
            return ToJsonStream(obj, stream, Encoding.UTF8);
        }

        public static Result ToJsonStream(object obj, Stream stream, Encoding encoding)
        {
            if (stream == null || encoding == null) return Result.INVALID_PARAMETERS;
            if (!stream.CanWrite) return Result.Failed("流不可写");
            try { WriteToStream(obj, stream, encoding); }
            catch (Exception e) { return Result.Failed(e); }
            return Result.OK;
        }

        public static Result ToJsonFile(object obj, string path)
        {
            if (string.IsNullOrEmpty(path)) return Result.INVALID_PARAMETERS;
            try { using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write)) return ToJsonStream(obj, fs); }
            catch (Exception e) { return Result.Failed(e); }
        }

        public static Result<JsonItem> ToJsonItem(object obj)
        {
            try { return Result<JsonItem>.Success(BuildJsonItem(obj)); }
            catch (Exception e) { return Result<JsonItem>.Failed(e); }
        }

        private static JsonItem BuildJsonItem(object obj)
        {
            if (obj == null) return new JsonItem();
            if (obj is JsonItem) return (JsonItem)obj;
            if (obj is string) return new JsonItem(obj as string);
            if (obj is bool) return new JsonItem((bool)obj);
            if (obj.GetType().IsNumber()) return new JsonItem(obj is double ? (double)obj : double.Parse(obj.ToString()));
            if (obj is IDictionary) return Dict2Json(obj);
            if (obj is IEnumerable) return Array2Json(obj);
            return Obj2Json(obj);
        }

        private static void ToString(object obj, StringBuilder sb)
        {
            if (obj == null) sb.Append("null");
            else if (obj is JsonItem) obj = ((JsonItem)obj).Value;
            else if (obj is bool) sb.Append((bool)obj ? "true" : "false");
            else if (obj is string) sb.Append('"').Append(obj).Append('"');
            else if (obj.GetType().IsNumber()) sb.Append(obj);
            else if (obj is IDictionary) Dict2Json(obj, sb);
            else if (obj is IEnumerable) Array2Json(obj, sb);
            else Obj2Json(obj, sb);
        }

        private static void WriteToStream(object obj, Stream stream, Encoding encoding)
        {
            if (obj == null) Write(stream, "null", encoding);
            else if (obj is JsonItem) obj = ((JsonItem)obj).Value;
            else if (obj is string)
            {
                Write(stream, "\"", encoding);
                var data = encoding.GetBytes(obj as string);
                stream.Write(data, 0, data.Length);
                Write(stream, "\"", encoding);
            }
            else if (obj is bool)
            {
                if ((bool)obj) Write(stream, "true", encoding);
                else Write(stream, "false", encoding);
            }
            else if (obj.GetType().IsNumber())
            {
                var data = encoding.GetBytes(obj.ToString());
                stream.Write(data, 0, data.Length);
            }
            else if (obj is IDictionary) Dict2Json(obj, stream, encoding);
            else if (obj is IEnumerable) Array2Json(obj, stream, encoding);
            else Obj2Json(obj, stream);
        }

        private static JsonObject Dict2Json(object dict, object json = null, Encoding encoding = null)
        {
            var iter = ((IDictionary)dict).GetEnumerator();
            if (json == null)
            {
                var val = new JsonObject();
                while (iter.MoveNext())
                {
                    var key = iter.Key?.ToString();
                    if (string.IsNullOrEmpty(key)) continue;
                    val.Add(key, BuildJsonItem(iter.Value));
                }
                return val;
            }
            var f = false;
            if (json is StringBuilder)
            {
                var sb = json as StringBuilder;
                sb.Append('{');
                while (iter.MoveNext())
                {
                    var key = iter.Key?.ToString();
                    if (string.IsNullOrEmpty(key)) continue;
                    if (f) sb.Append(',');
                    else f = true;
                    sb.Append('"').Append(key).Append('"').Append(':');
                    ToString(iter.Value, sb);
                }
                sb.Append('}');
            }
            else if (json is Stream)
            {
                var stream = json as Stream;
                Write(stream, "{", encoding);
                while (iter.MoveNext())
                {
                    var key = iter.Key?.ToString();
                    if (string.IsNullOrEmpty(key)) continue;
                    if (f) Write(stream, ",", encoding);
                    else f = true;
                    Write(stream, "\"", encoding);
                    var data = encoding.GetBytes(key);
                    stream.Write(data, 0, data.Length);
                    Write(stream, "\"", encoding);
                    Write(stream, ":", encoding);
                    WriteToStream(iter.Value, stream, encoding);
                }
                Write(stream, "}", encoding);
            }
            return null;
        }

        private static JsonArray Array2Json(object list, object json = null, Encoding encoding = null)
        {
            var cols = (IEnumerable)list;
            if (json == null)
            {
                var val = new JsonArray();
                foreach (var item in cols) val.Add(BuildJsonItem(item));
                return val;
            }
            if (json is StringBuilder)
            {
                var sb = json as StringBuilder;
                sb.Append('[');
                var f = false;
                foreach (var item in cols)
                {
                    if (f) sb.Append(',');
                    else f = true;
                    ToString(item, sb);
                }
                sb.Append(']');
            }
            else if (json is Stream)
            {
                var stream = json as Stream;
                Write(stream, "[", encoding);
                var f = false;
                foreach (var item in cols)
                {
                    if (f) Write(stream, ",", encoding);
                    else f = true;
                    WriteToStream(item, stream, encoding);
                }
                Write(stream, "]", encoding);
            }
            return null;
        }

        private static JsonObject Obj2Json(object obj, object json = null, Encoding encoding = null)
        {
            var fields = new Dictionary<string, FieldInfo>();
            var props = new Dictionary<string, PropertyInfo>();
            EnumAllMembers(obj.GetType(), fields, props);
            if (json == null)
            {
                var val = new JsonObject();
                foreach (var pair in fields) val.Add(pair.Key, BuildJsonItem(pair.Value.GetValue(obj)));
                foreach (var pair in props) val.Add(pair.Key, BuildJsonItem(pair.Value.GetValue(obj)));
                return val;
            }
            var f = false;
            if (json is StringBuilder)
            {
                var sb = json as StringBuilder;
                sb.Append('{');
                foreach (var pair in fields)
                {
                    if (f) sb.Append(',');
                    else f = true;
                    sb.Append('"').Append(pair.Key).Append('"').Append(':');
                    ToString(pair.Value.GetValue(obj), sb);
                }
                foreach (var pair in props)
                {
                    if (!pair.Value.CanRead) continue;
                    if (f) sb.Append(',');
                    else f = true;
                    sb.Append('"').Append(pair.Key).Append('"').Append(':');
                    ToString(pair.Value.GetValue(obj), sb);
                }
                sb.Append('}');
            }
            else if (json is Stream)
            {
                var stream = json as Stream;
                Write(stream, "{", encoding);
                foreach (var pair in fields)
                {
                    if (f) Write(stream, ",", encoding);
                    else f = true;
                    Write(stream, "\"", encoding);
                    var data = encoding.GetBytes(pair.Key);
                    stream.Write(data, 0, data.Length);
                    Write(stream, "\"", encoding);
                    Write(stream, ":", encoding);
                    WriteToStream(pair.Value.GetValue(obj), stream, encoding);
                }
                foreach (var pair in props)
                {
                    if (!pair.Value.CanRead) continue;
                    if (f) Write(stream, ",", encoding);
                    else f = true;
                    Write(stream, "\"", encoding);
                    var data = encoding.GetBytes(pair.Key);
                    stream.Write(data, 0, data.Length);
                    Write(stream, "\"", encoding);
                    Write(stream, ":", encoding);
                    WriteToStream(pair.Value.GetValue(obj), stream, encoding);
                }
                Write(stream, "}", encoding);
            }
            return null;
        }
        #endregion

        #region ? To Object
        public static Result<JsonItem> Parse(string text)
        {
            if (string.IsNullOrEmpty(text)) return Result<JsonItem>.Failed("Json为空");
            var ps = new CharParser();
            try
            {
                ps.Next(text.ToCharArray());
                return Result<JsonItem>.Success(ps.Get());
            }
            catch (Exception e) { return Result<JsonItem>.Failed(e); }
        }

        public static Result<T> Parse<T>(string text)
        {
            if (string.IsNullOrEmpty(text)) return Result<T>.Failed("Json为空");
            var ps = new CharParser();
            try
            {
                ps.Next(text.ToCharArray());
                return ps.Get().To<T>();
            }
            catch (Exception e) { return Result<T>.Failed(e); }
        }

        public static Result<JsonItem> Parse(Stream stream)
        {
            return Parse(stream, Encoding.UTF8);
        }

        public static Result<JsonItem> Parse(Stream stream, Encoding encoding)
        {
            if (stream == null || encoding == null) return Result<JsonItem>.INVALID_PARAMETERS;
            if (!stream.CanRead) return Result<JsonItem>.Failed("流不可读");
            var ps = new CharParser();
            try
            {
                var decoder = encoding.GetDecoder();
                int len;
                var data = new byte[4096];
                var res = new char[4096];
                while ((len = stream.Read(data, 0, data.Length)) > 0)
                {
                    decoder.Convert(data, 0, len, res, 0, res.Length, false, out int u1, out int u2, out bool u3);
                    ps.Next(res, u2);
                }
                return Result<JsonItem>.Success(ps.Get());
            }
            catch (Exception e) { return Result<JsonItem>.Failed(e); }
        }

        public static Result<T> Parse<T>(Stream stream)
        {
            return Parse<T>(stream, Encoding.UTF8);
        }

        public static Result<T> Parse<T>(Stream stream, Encoding encoding)
        {
            if (stream == null) return Result<T>.INVALID_PARAMETERS;
            if (!stream.CanRead) return Result<T>.Failed("流不可读");
            var ps = new CharParser();
            try
            {
                var decoder = encoding.GetDecoder();
                int len;
                var data = new byte[4096];
                var res = new char[4096];
                while ((len = stream.Read(data, 0, data.Length)) > 0)
                {
                    decoder.Convert(data, 0, len, res, 0, res.Length, false, out int u1, out int u2, out bool u3);
                    ps.Next(res, u2);
                }
                return ps.Get().To<T>();
            }
            catch (Exception e) { return Result<T>.Failed(e); }
        }

        public static Result<JsonItem> ParseFile(string path)
        {
            if (string.IsNullOrEmpty(path)) return Result<JsonItem>.INVALID_PARAMETERS;
            if (!File.Exists(path)) return Result<JsonItem>.Failed("文件不存在");
            try { using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read)) return Parse(fs); }
            catch (Exception e) { return Result<JsonItem>.Failed(e); }
        }

        public static Result<T> ParseFile<T>(string path)
        {
            if (string.IsNullOrEmpty(path)) return Result<T>.INVALID_PARAMETERS;
            if (!File.Exists(path)) return Result<T>.Failed("文件不存在");
            try { using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read)) return Parse<T>(fs); }
            catch (Exception e) { return Result<T>.Failed(e); }
        }
        #endregion

        #region JsonItem Enhance
        public static Result<T> To<T>(this JsonItem item)
        {
            try
            {
                return Result<T>.Success((T)To(item, typeof(T)));
            }
            catch (Exception e) { return Result<T>.Failed(e); }
        }

        public static Result CopyTo(this JsonItem item, Stream stream)
        {
            return ToJsonStream(item, stream);
        }

        public static Result CopyTo(this JsonItem item, string path)
        {
            return ToJsonFile(item, path);
        }
        #endregion

        #region JsonItem To Object
        private static readonly Type TYPE_STRING = typeof(string);
        private static readonly Type TYPE_BOOL = typeof(bool);
        private static readonly Type TYPE_DECIMAL = typeof(decimal);
        private static readonly Type TYPE_DOUBLE = typeof(double);
        private static readonly Type TYPE_FLOAT = typeof(float);
        private static readonly Type TYPE_INT = typeof(int);
        private static readonly Type TYPE_SHORT = typeof(short);
        private static readonly Type TYPE_LONG = typeof(long);
        private static readonly Type TYPE_BYTE = typeof(byte);
        private static readonly Type TYPE_UINT = typeof(uint);
        private static readonly Type TYPE_USHORT = typeof(ushort);
        private static readonly Type TYPE_ULONG = typeof(ulong);
        private static readonly Type TYPE_SBYTE = typeof(sbyte);
        private static object To(JsonItem item, Type type)
        {
            if (item == null) return default;
            if (type == null) throw new Exception("转换目标类型为空");
            if (item.Type == JsonType.Object) return ToObject(item.Value as Dictionary<string, JsonItem>, type);
            if (item.Type == JsonType.Array) return ToArray(item.Value as List<JsonItem>, type);
            if (TYPE_STRING.Equals(type)) return item.Value?.ToString();
            if (TYPE_BOOL.Equals(type))
            {
                if (item.Type == JsonType.Boolean) return item.Value;
            }
            else if (type.IsNumber())
            {
                if (item.Type == JsonType.Number)
                {
                    var val = (double)item.Value;
                    if (TYPE_DOUBLE.Equals(type)) return val;
                    if (TYPE_DECIMAL.Equals(type)) return Convert.ToDecimal(val);
                    if (TYPE_FLOAT.Equals(type)) return Convert.ToSingle(val);
                    if (val == Math.Floor(val))
                    {
                        if (TYPE_INT.Equals(type)) return Convert.ToInt32(val);
                        if (TYPE_SHORT.Equals(type)) return Convert.ToInt16(val);
                        if (TYPE_LONG.Equals(type)) return Convert.ToInt64(val);
                        if (TYPE_BYTE.Equals(type)) return Convert.ToByte(val);
                        if (TYPE_UINT.Equals(type)) return Convert.ToUInt32(val);
                        if (TYPE_USHORT.Equals(type)) return Convert.ToUInt16(val);
                        if (TYPE_ULONG.Equals(type)) return Convert.ToUInt64(val);
                        if (TYPE_SBYTE.Equals(type)) return Convert.ToSByte(val);
                    }
                }
            }
            else if (item.Type == JsonType.Null) return default;
            throw new Exception($"无法将类型 ${item.Type} 转换到类型 ${type.Name}");
        }

        private static object ToObject(Dictionary<string, JsonItem> values, Type type)
        {
            var obj = NewInstance(type);
            if (obj == null) throw new Exception($"无法实例化 {type.Name} 对象");
            Type gt;
            if ((gt = GetGenericTypeOfDict(type)) != null)
            {
                var method = type.GetMethod("Add", new Type[] { TYPE_STRING, gt });
                foreach (var pair in values) method.Invoke(obj, new object[] { pair.Key, To(pair.Value, gt) });
                return obj;
            }
            var fields = new Dictionary<string, FieldInfo>();
            var props = new Dictionary<string, PropertyInfo>();
            EnumAllMembers(type, fields, props);
            foreach (var pair in values)
            {
                if (fields.TryGetValue(pair.Key, out FieldInfo field))
                    field.SetValue(obj, To(pair.Value, field.FieldType) ?? default);
                else if (props.TryGetValue(pair.Key, out PropertyInfo prop) && prop.CanWrite)
                    prop.SetValue(obj, To(pair.Value, prop.PropertyType) ?? default);
            }
            return obj;
        }

        private static object ToArray(List<JsonItem> values, Type type)
        {
            if (type.IsArray)
            {
                if (type.GetArrayRank() == 1)
                {
                    var gt = type.GetElementType();
                    var array = Array.CreateInstance(gt, values.Count);
                    var i = 0;
                    foreach (var val in values) array.SetValue(To(val, gt), i++);
                    return array;
                }
            }
            else
            {
                Type gt;
                if ((gt = GetGenericTypeOfCol(type)) != null)
                {
                    var collection = Activator.CreateInstance(type);
                    var method = type.GetMethod("Add", new Type[] { gt });
                    foreach (var val in values) method.Invoke(collection, new object[] { To(val, gt) });
                    return collection;
                }
            }
            throw new Exception($"无法将数组转换到类型 ${type.Name}");
        }

        private static Type GetGenericTypeOfCol(Type type)
        {
            if (type == null) return null;
            Type col;
            foreach (var t in type.GetInterfaces())
            {
                if (t.IsGenericType && typeof(ICollection<>).Equals(t.GetGenericTypeDefinition()))
                    return t.GenericTypeArguments[0];
                if ((col = GetGenericTypeOfCol(t)) != null) return col;
            }
            return GetGenericTypeOfCol(type.BaseType);
        }

        private static Type GetGenericTypeOfDict(Type type)
        {
            if (type == null) return null;
            Type col;
            foreach (var t in type.GetInterfaces())
            {
                if (t.IsGenericType && typeof(IDictionary<,>).Equals(t.GetGenericTypeDefinition()) && TYPE_STRING.Equals(t.GenericTypeArguments[0]))
                    return t.GenericTypeArguments[1];
                if ((col = GetGenericTypeOfDict(t)) != null) return col;
            }
            return GetGenericTypeOfDict(type.BaseType);
        }
        #endregion

        #region Common
        private static void EnumAllMembers(Type type, Dictionary<string, FieldInfo> fields, Dictionary<string, PropertyInfo> properties)
        {
            if (type == null) return;
            AddMember(type.GetFields(), fields);
            AddMember(type.GetProperties(), properties);
            EnumAllMembers(type.BaseType, fields, properties);
        }

        private static void AddMember<T>(T[] array, Dictionary<string, T> map) where T : MemberInfo
        {
            foreach (var member in array)
            {
                if (member.GetCustomAttribute(typeof(JsonIgnore)) != null) continue;
                var key = member.Name;
                var mem = member.GetCustomAttribute(typeof(JsonMember)) as JsonMember;
                if (mem != null && !string.IsNullOrEmpty(mem.Alias)) key = mem.Alias;
                if (!map.ContainsKey(key)) map.Add(key, member);
            }
        }

        private static object NewInstance(Type type)
        {
            var list = new List<KeyValuePair<ConstructorInfo, ParameterInfo[]>>();
            foreach (var cons in type.GetConstructors()) list.Add(new KeyValuePair<ConstructorInfo, ParameterInfo[]>(cons, cons.GetParameters()));
            if (list.Count == 0) return null;
            list.Sort((p1, p2) => p1.Value.Length - p2.Value.Length);
            foreach (var pair in list)
            {
                try
                {
                    var para = new object[pair.Value.Length];
                    for (var i = 0; i < para.Length; i++) para[i] = default;
                    return pair.Key.Invoke(para);
                }
                catch { }
            }
            return null;
        }

        private static void Write(Stream stream, string text, Encoding encoding)
        {
            var data = encoding.GetBytes(text);
            stream.Write(data, 0, data.Length);
        }
        #endregion

        #region String To JsonItem
        private static readonly char[] EMPTY = new char[] { '\a', '\r', '\b', '\n', '\v', '\t', '\f' };
        private static readonly char[] BOOL = new char[] { 'r', 'u', 'e', 'a', 'l', 's' };
        private static readonly char[] NULL = new char[] { 'u', 'l' };
        private static readonly char[] ESCAPE = new char[] { 'a', 'r', 'b', 'n', 'v', 't', 'f', '"', '\\', '/'};

        private abstract class Node
        {
            private static readonly IllegalJsonException ex = new IllegalJsonException(nameof(Node));
            public JsonItem Current;
            public bool End;
            protected Node Prev;
            protected int State;
            public void Next(char ch, ref Node node)
            {
                if (End)
                {
                    if (ch != ' ' && !ch.In(EMPTY)) throw ex;
                }
                else
                {
                    if ((node = Next(ch)) == this) return;
                    if (node != null) node.Prev = this;
                    else
                    {
                        if (this is ValNode) Current = (this as ValNode).Parse();
                        if (Prev == null)
                        {
                            End = true;
                            node = this;
                            if (this is ValNode && ch != ' ' && !ch.In(EMPTY)) throw ex;
                        }
                        else
                        {
                            node = Prev;
                            node.Back(Current);
                            if (this is ValNode) node.Next(ch, ref node);
                        }
                    }
                }
            }
            protected abstract Node Next(char ch); //return this, new Into, null Back
            protected virtual void Back(JsonItem val) {}
        }
        private class ValNode : Node
        {
            private static readonly IllegalJsonException ex = new IllegalJsonException(nameof(ValNode));
            private StringBuilder value = new StringBuilder();
            public ValNode(char ch) { Next(ch); }
            protected override Node Next(char ch)
            {
                if (State == 0) 
                {
                    if (ch.In(EMPTY) || ch == ' ') return this;
                    if (ch == 'f' || ch == 't') State = 1;
                    else if (ch == 'n') State = 2;
                    else if (ch.IsNumber()) State = 3;
                    else throw ex;
                    value.Append(ch);
                }
                else if (State == 1)
                {
                    if (ch.In(BOOL)) value.Append(ch);
                    else return null;
                }
                else if (State == 2)
                {
                    if (ch.In(NULL)) value.Append(ch);
                    else return null;
                }
                else
                {
                    if (ch.IsNumber()) value.Append(ch);
                    else if (ch != '.') return null;
                    if (State == 3) State = 4;
                    else throw ex;
                    value.Append(ch);
                }
                return this;
            }
            public JsonItem Parse()
            {
                if (State > 0)
                {
                    var t = value.ToString();
                    if (State == 1)
                    {
                        if ("true".Equals(t)) return new JsonItem(true);
                        if ("false".Equals(t)) return new JsonItem(false);
                    }
                    else if (State == 2)
                    {
                        if ("null".Equals(t)) return new JsonItem();
                    }
                    else if (t.ParseDouble(out double d)) return new JsonItem(d);
                }
                throw ex;
            }
        }
        private class StringNode : Node
        {
            private static readonly IllegalJsonException ex = new IllegalJsonException(nameof(StringNode));
            private StringBuilder value = new StringBuilder();
            private StringBuilder unicode;
            protected override Node Next(char ch)
            {
                if (State == 0)
                {
                    if (ch == '"')
                    {
                        Current = new JsonItem(value.ToString());
                        return null;
                    }
                    else if (ch == '\\') State = 1;
                    else value.Append(ch);
                }
                else if (State == 1)
                {
                    if (ch == 'u')
                    {
                        State = 2;
                        if (unicode == null) unicode = new StringBuilder();
                        else unicode.Clear();
                    }
                    else
                    {
                        var i = ch.Where(ESCAPE);
                        if (i == -1) throw ex;
                        if (i < EMPTY.Length) value.Append(EMPTY[i]);
                        else value.Append(ch);
                        State = 0;
                    }
                }
                else if (ch.IsHex())
                {
                    unicode.Append(ch);
                    if (unicode.Length == 4)
                    {
                        State = 0;
                        value.Append((char)int.Parse(unicode.ToString(), System.Globalization.NumberStyles.HexNumber));
                    }
                }
                else throw ex;
                return this;
            }
        }
        private class ObjectNode : Node
        {
            private static readonly IllegalJsonException ex = new IllegalJsonException(nameof(ObjectNode));
            private string key;
            public ObjectNode()
            {
                Current = new JsonObject();
            }
            protected override Node Next(char ch)
            {
                if (ch == ' ' || ch.In(EMPTY)) return this;
                if (State == 0)
                {
                    if (ch == '}') return null;
                    if (ch != '"') throw ex;
                    State = 1;
                    return new StringNode();
                }
                else if (State == 1)
                {
                    if (ch != ':') throw ex;
                    State = 2;
                    return this;
                }
                else if (State == 2)
                {
                    if (ch == '{') return new ObjectNode();
                    if (ch == '[') return new ArrayNode();
                    if (ch == '"') return new StringNode();
                    if (ch == 't' || ch == 'f' || ch == 'n' || ch.IsNumber()) return new ValNode(ch);
                }
                else if (State == 3)
                {
                    if (ch == '}') return null;
                    if (ch != ',') throw ex; 
                    State = 4;
                    return this;
                }
                else if(ch == '"')
                {
                    State = 1;
                    return new StringNode();
                }
                throw ex;
            }

            protected override void Back(JsonItem val)
            {
                if (State == 1)
                {
                    key = val.Value as string;
                    if (string.IsNullOrEmpty(key)) throw ex;
                }
                else
                {
                    (Current as JsonObject).Add(key, val);
                    State = 3;
                }
            }
        }
        private class ArrayNode : Node
        {
            private static readonly IllegalJsonException ex = new IllegalJsonException(nameof(ArrayNode));
            public ArrayNode()
            {
                Current = new JsonArray();
            }
            protected override Node Next(char ch)
            {
                if (ch == ' ' || ch.In(EMPTY)) return this;
                if (State == 0)
                {
                    if (ch == ']') return null;
                    if (ch == ',') return this;
                    if (ch == '{') return new ObjectNode();
                    if (ch == '[') return new ArrayNode();
                    if (ch == '"') return new StringNode();
                    if (ch == 't' || ch == 'f' || ch == 'n' || ch.IsNumber()) return new ValNode(ch);
                }
                else
                {
                    if (ch == ']') return null;
                    if (ch != ',') throw ex;
                    State = 0;
                    return this;
                }
                throw ex;
            }

            protected override void Back(JsonItem val)
            {
                if (State == 0)
                {
                    (Current as JsonArray).Add(val);
                    State = 1;
                }
            }
        }
        private class CharParser
        {
            private static readonly IllegalJsonException ex = new IllegalJsonException(nameof(CharParser));
            private Node node;
            private long pos;
            public void Next(char[] chs, int len = -1)
            {
                if (len < 0) len = chs.Length;
                for (var i = 0; i < len; i++)
                {
                    var ch = chs[i];
                    try
                    {
                        if (node != null) node.Next(ch, ref node);
                        else if (ch == '{') node = new ObjectNode();
                        else if (ch == '[') node = new ArrayNode();
                        else if (ch == '"') node = new StringNode();
                        else if (ch == 't' || ch == 'f' || ch == 'n' || ch.IsNumber()) node = new ValNode(ch);
                        else if (ch != ' ' && !ch.In(EMPTY)) throw ex;
                        pos++;
                    }
                    catch (IllegalJsonException e)
                    {
#if DEBUG
                        throw new Exception($"Json不合法, 非法字符 = {ch}, pos = {pos}, name = {e.Name}");
#else
                        throw new Exception($"Json不合法, 非法字符 = {ch}, pos = {pos}");
#endif
                    }
                }
            }
            public JsonItem Get()
            {
                if (node == null) throw ex;
                if (node.End) return node.Current;
                else if (node is ValNode) return (node as ValNode).Parse();
                throw ex;
            }
        }
        private class IllegalJsonException : Exception
        {
            private readonly string name;
            internal string Name { get => name; }
            public IllegalJsonException(string name) : base()
            {
                this.name = name;
            }
        }
#endregion
    }
}
